package com.lagou.edu.factory;

import com.lagou.edu.annotation.MyAutowired;
import com.lagou.edu.annotation.MyService;
import com.lagou.edu.annotation.MyTransactional;
import com.mysql.jdbc.StringUtils;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 工厂类
 * 利用反射生产对象
 * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
 * 任务二：对外提供获取实例对象的接口（根据id获取）
 */
public class BeanFactory {
    private static Map<String,Object> map = new HashMap<>();  // 存储对象

    static {
        try {
            // 通过反射技术，扫描包并获取反射对象集合
            Reflections reflections = new Reflections("com.lagou.edu");
            Set<Class<?>> clazzs = reflections.getTypesAnnotatedWith(MyService.class);
            // 遍历对象集合
            for (Class<?> clazz : clazzs) {
                // 获取实例对象
                Object object = clazz.newInstance();
                MyService service = clazz.getAnnotation(MyService.class);

                // 判断MyService注解上是否有自定义对象ID
                if (StringUtils.isNullOrEmpty(service.value())) {
                    // 由于getName获取的是全限定类名，因此要分割去除掉前面的包名部分
                    String[] split = clazz.getName().split("\\.");
                    map.put(split[split.length - 1], object);
                } else {
                    map.put(service.value(), object);
                }
            }

            // 维护对象之间依赖关系
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                Object obj = entry.getValue();
                Class<?> aClass = obj.getClass();
                // 获取每个类的所有属性
                Field[] fields = aClass.getDeclaredFields();
                // 遍历属性，确认是否有使用Autowired注解，有则需要完成注入
                for (Field field : fields) {
                    // 判断是否使用注解的参数
                    if (field.isAnnotationPresent(MyAutowired.class)
                        && field.getAnnotation(MyAutowired.class).value()) {
                        String[] split = field.getType().getName().split("\\.");
                        String name = split[split.length - 1];
                        Method[] methods = aClass.getMethods();
                        for (int index = 0; index < methods.length; ++index) {
                            Method method = methods[index];
                            if (method.getName().equalsIgnoreCase("set" + name)) {
                                method.invoke(obj, map.get(name));
                            }
                        }
                    }
                }

                // 判断当前类是否有Transactional注解，若有则使用代理对象
                if (aClass.isAnnotationPresent(MyTransactional.class)) {
                    // 获取代理工厂
                    ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
                    Class<?>[] interfaces = aClass.getInterfaces();
                    // 判断对象是否实现接口
                    if (interfaces != null && interfaces.length > 0) {
                        // 实现了接口，使用JDK
                        obj = proxyFactory.getJdkProxy(obj);
                    } else {
                        // 未实现接口，则使用CGLIB
                        obj = proxyFactory.getCglibProxy(obj);
                    }
                }

                // 把处理之后的object重新放入map中
                map.put(entry.getKey(), obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据ID获取对象
     * @param id    ID
     * @return      对象
     */
    public static Object getBean(String id) {
        return map.get(id);
    }
}
